<template>
  <div class="vk-tooltip" v-on="outerEvents" ref="popperContainerNode">
    <!-- 触发的内容 -->
    <div class="vk-tooltip__trigger" ref="triggerNode" v-on="events">
      <slot></slot>
    </div>
    <!-- 展示的内容 -->
    <transition :name="transition">
      <div v-if="isOpen" class="vk-tooltip__popper" ref="overlayNode">
        <slot name="content">{{ content }}</slot>
        <div id="arrow" data-popper-arrow></div>
      </div>
    </transition>
  </div>
</template>

<script setup lang="ts">
import {
  defineOptions,
  defineProps,
  withDefaults,
  ref,
  watch,
  reactive,
  defineExpose,
  onUnmounted,
  computed,
} from "vue";
import { TooltipEmits, TooltipProps, TooltipInstance } from "./types";
import { createPopper, placements } from "@popperjs/core";
import type { Instance } from "@popperjs/core";
import useClickOutside from "../../hooks/useClickOutside";
import { debounce } from "lodash-es";

defineOptions({
  name: "VkTooltip",
});

const props = withDefaults(defineProps<TooltipProps>(), {
  placement: "left",
  trigger: "hover",
  transition: "fade",
  openDelay: 0,
  closeDelay: 0,
});

const emits = defineEmits<TooltipEmits>();

const isOpen = ref(false);
const overlayNode = ref<HTMLElement>();
const triggerNode = ref<HTMLElement>();
const popperContainerNode = ref<HTMLElement>();

let popperInstance: Instance | null = null; // 定义一个 popper 实例
// 给events定义为 Record类型，它的key是string，值是any类型
let events: Record<string, any> = reactive({});
let outerEvents: Record<string, any> = reactive({});
let openTimes = 0;
let closeTimes = 0;

const popperOptions = computed(() => {
  return {
    placements: props.placement,
    ...props.popperOptions,
  };
});

const open = () => {
  isOpen.value = true;
  emits("visible-change", true);
  openTimes++;
  //   console.log("openTimes-->", openTimes);
};

const close = () => {
  isOpen.value = false;
  emits("visible-change", false);
  closeTimes++;
  //   console.log("closeTimes-->", closeTimes);
};

const openDebounce = debounce(open, props.openDelay);
const closeDebounce = debounce(close, props.closeDelay);

// 显示时，不应该触发close()
const openFinal = () => {
  closeDebounce.cancel(); // debounce 可以使用 cancel()来取消
  openDebounce();
};

// 隐藏时，不应该触发open()
const closeFinal = () => {
  openDebounce.cancel();
  closeDebounce();
};

const togglePopper = () => {
  if (isOpen.value) {
    closeFinal();
  } else {
    openFinal();
  }
};

const attachEvents = () => {
  if (props.trigger === "hover") {
    events["mouseenter"] = openFinal;
    outerEvents["mouseleave"] = closeFinal;
  } else if (props.trigger === "click") {
    events["click"] = togglePopper;
  }
};

// 如果支持手动触发，才绑定这些事件
if (!props.manual) {
  attachEvents(); // 在 setup 中手动调用，就可以把这些对应的事件绑定上去了
}

useClickOutside(popperContainerNode, () => {
  // 当点击到tooltip容器外侧时，
  if (props.trigger === "click" && isOpen.value && !props.manual) {
    closeFinal();
  }
  if (isOpen.value) {
    emits("click-outside", true);
  }
});

watch(
  () => props.manual,
  (isManual) => {
    if (isManual) {
      // 如果开启了手动触发模式，就清空这些事件
      events = {};
      outerEvents = {};
    } else {
      // 否则重新绑定事件
      attachEvents();
    }
  }
);

watch(
  () => props.trigger,
  (newTrigger, oldTrigger) => {
    if (newTrigger !== oldTrigger) {
      events = {};
      outerEvents = {};
      attachEvents();
    }
  }
);
// 监听 isOpen，控制是否显示
watch(
  isOpen,
  (newValue) => {
    if (newValue) {
      if (overlayNode.value && triggerNode.value) {
        popperInstance = createPopper(
          triggerNode.value,
          overlayNode.value,
          popperOptions.value
        );
      } else {
        popperInstance?.destroy; // 销毁实例
      }
    }
  },
  { flush: "post" } // 当dom节点生成时再进行监听
);

defineExpose<TooltipInstance>({
  show: openFinal,
  hide: closeFinal,
});

onUnmounted(() => {
  popperInstance?.destroy;
});
</script>

<style scoped></style>
